package com.itqf.controller;


import com.itqf.bean.DtsAdmin;
import com.itqf.captcha.CaptchaCodeManager;
import com.itqf.dto.DtsAdminDto;
import com.itqf.service.DtsPermissionService;
import com.itqf.service.DtsRoleService;
import com.itqf.util.Base64;
import com.itqf.util.ResponseUtil;

import com.itqf.utils.*;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.*;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.*;

/**
 * projectName: shop_manage
 *
 * @author: MaTianWei
 * time: 2021/7/13 9:40
 * description:后台认证模块接口，认证路径，验证码，登录，用户信息获取
 */
@RestController
@CrossOrigin("*")
@RequestMapping("admin/auth")
public class AdminAuthController {
    //日志
    public static final Logger logger = LoggerFactory.getLogger(AdminAuthController.class);


    @Autowired
    private DtsRoleService dtsRoleService;
    @Autowired
    private DtsPermissionService dtsPermissionService;
    @Autowired
    private ApplicationContext applicationContext;


    //认证失败
    @GetMapping("401")
    public Object ret401(){
        logger.info("[认证失败]接口调用");
        return ResponseUtil.unlogin();
    }

    //登录成功对应的接口
    @GetMapping("index")
    public Object reIndex(){
        logger.info("[登录成功]出发接口跳转");
        return ResponseUtil.ok();
    }


    //授权失败
    @GetMapping("403")
    public Object ret403(){
        logger.info("[授权失败]接口调用");
        return ResponseUtil.unauthz();
    }


    //生成验证码图片接口
    @GetMapping("captchaImage")
    public Object retCaptcha() throws IOException{
        logger.info("[验证码生成]接口开始调用");
        //1·生成验证码 需要的原材料 code、uuid
         String code = VerifyCodeUtils.generateVerifyCode(4);
         String uuid = UUID.randomUUID().toString().replaceAll("_","");

         logger.debug("[验证码生成]验证码为:{}，uuid为:{}");
         //2·将验证码封装成CaptchaItem并储存到CaptchaManage中
        boolean cache = CaptchaCodeManager.addToCache(uuid, code, 5);
        if (!cache) {
            //验证码存储失败
            logger.error("[验证码生成]生成的验证码:{}已经存在,请稍后在试");
            //程序终止
            return AdminResponseUtil.fail(AdminResponseCode.AUTH_CAPTCHA_ERROR);
        }
        //3·生成验证码图片Base64
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            VerifyCodeUtils.outputImage(110,40,baos,code);
        } catch (IOException e) {
            logger.error("[验证码生成]验证码生成图片失败!");
            throw e;
        }
        String imStr = Base64.encode(baos.toByteArray());
        //4·生成返回的结果数据
        Map data = new HashMap();
        data.put("img",imStr);
        data.put("uuid",uuid);
        logger.info("[验证码生成]接口调用结束，返回数据为：{}！",data);
        return ResponseUtil.ok(data);
    }







    /*
    bean
        1·实体类
    pojo
        实体类 持久化 数据库挂钩
    vo
        view Object视图解析对象 返回给前端

    dto
        数据传输对象
     */




    //界面登录
    @PostMapping(value = "login",consumes = "application/json;charset=utf-8")
    public Object login(@RequestBody DtsAdminDto dtsAdminDto){
        //日志
        logger.info("[用户登录]登录接口开始调用");
        logger.debug("[用户登录]用户输入 的信息为：{}",dtsAdminDto);

        //1·判断参数
        if (dtsAdminDto == null || StringUtils.isEmpty(dtsAdminDto.getUsername())
                || StringUtils.isEmpty(dtsAdminDto.getPassword())
                || StringUtils.isEmpty(dtsAdminDto.getCode())
                || StringUtils.isEmpty(dtsAdminDto.getUuid()))
        {
            logger.warn("[用户登录]登录的数据有为null的");
            //参数异常
            return ResponseUtil.badArgument();
        }
        //2·判断验证
        //获取缓存的验证码
        String code = CaptchaCodeManager.getCachedCaptcha(dtsAdminDto.getUuid());
        if (code == null) {
            logger.warn("[用户登录]本地缓存验证码失效");
        return AdminResponseUtil.fail(AdminResponseCode.AUTH_CAPTCHA_EXPIRED);
    }
        if (!code.equalsIgnoreCase(dtsAdminDto.getCode())) {
            logger.warn("[用户登录]验证码校验失败用户输入：{}，正确的是：{}",dtsAdminDto.getCode(),code);
            return AdminResponseUtil.fail(AdminResponseCode.AUTH_CAPTCHA_ERROR);
        }
        //3·认证过程
        //封装toke
        UsernamePasswordToken token = new UsernamePasswordToken(dtsAdminDto.getUsername(),dtsAdminDto.getPassword());
        //subject认证login
        Subject subject = SecurityUtils.getSubject();
        try{
            subject.login(token);
        }catch (AuthenticationException e){
            logger.error("[用户登录]输入的账号或者密码错误");
            return AdminResponseUtil.fail(AdminResponseCode.ADMIN_INVALID_ACCOUNT_OR_PASSWORD);
        }
        //4·返回认证结果
        //获取用户的唯一标识
        Serializable id = subject.getSession().getId();
        return ResponseUtil.ok(id);
    }







    //登陆后获取用户名权限详细信息地址
    //shiro需要认证
    @RequiresAuthentication
    @GetMapping("info")
    public Object info(){
        //1`获取dtsadmin
        Subject subject = SecurityUtils.getSubject();
        DtsAdmin dtsAdmin = (DtsAdmin) subject.getPrincipal();
        logger.debug("[用户信息]读取缓存的dtsAdmin对象为{}",dtsAdmin);
        //2`数据封装
        Map data = new HashMap();
        data.put("name",dtsAdmin.getUsername());
        data.put("avatar",dtsAdmin.getAvatar());
        //3`业务调用
        //查询角色
        List<String> roleNames = dtsRoleService.queryRoleNames(dtsAdmin.getRoleIds());

        if (roleNames == null) {

            logger.error("[用户信息]用户没有分配角色");

            return ResponseUtil.unauthz();//无权限操作
        }
        //查询数据库有的权限
        Set<String> permissionNames = dtsPermissionService.queryPermissionName(dtsAdmin.getRoleIds());
        //继续封装
        data.put("roles",roleNames);
        //参数一:IOC容器 ApplicationContext
        //参数二:具体的包
        String basePackage = "com.itqf.controller";
        List<Permission> permissionList = PermissionUtil.listPermission(applicationContext, basePackage);
        //处理放入map便于查询
        Map map = new HashMap();
        //所有功能和权限
        for (Permission permission : permissionList) {
            map.put(permission.getRequiresPermissions().value()[0],permission.getApi());
        }

        Set<String> urls = new HashSet<>();
        //遍历自己拥有的权限名字
        for (String name : permissionNames) {
            //检查我们的权限
            if(name.equalsIgnoreCase("*")){
                urls.clear();
                urls.add("*");
                break;
            }
            //检查项目是否拥有你返回的权限的功能
            if(map.containsKey(name)){
                urls.add((String) map.get(name));
            }
        }
        logger.debug("[用户信息]获取到的用户的权限地址为：{}",urls);
        data.put("perms",urls);
        return ResponseUtil.ok(data);
    }





    //退出登录
    @RequiresAuthentication
    @PostMapping("logout")
    public Object logout(){

        Subject subject = SecurityUtils.getSubject();
        subject.logout();

        logger.info("[退出登录]退出登录接口调用完成");

        return ResponseUtil.ok();

    }
}
